// gojs模块
import go from 'gojs'
// 基于gojs自定义的LinkTemplate
import BuildSplineTemplate from './template/link/spline'
import BuildLineTemplate from './template/link/line'
import BuildArcTemplate from './template/link/arc'
// 基于gojs自定义的LinkTemplate(带有动画效果)
import BuildAnimateSplineTemplate from './template/link/animate-spline'
import BuildAnimateLineTemplate from './template/link/animate-line'
import BuildAnimateArcTemplate from './template/link/animate-arc'
// 基于gojs自定义的NodeTemplate
import BuildEllipseTemplate from './template/node/ellipse'
// 基于gojs自定义arc分支形变工具(reshape)
import CurvedLinkReshapingTool from './extension/CurvedLinkReshapingTool'
// 基于gojs自定义分支标签拖拽工具(drag)
import LinkLabelDraggingTool from './extension/LinkLabelDraggingTool'
// 基于gojs自定义复制粘贴命令以及键盘方向键控制(默认粘贴时偏移20)
import DrawCommandHandler from './extension/DrawCommandHandler'
import Util from '../util'
import config from '../config'
import Store from '../store'
import { BUILD_ENTITY_EVENT_NAME } from '../internal'
import { makeFont, makeUUID } from '@/utils'
import { isObject, isFunction, clearObject } from '@/utils/types'
import { MessageBox } from 'element-ui'
import store from '../store'
import field from '../enum/field'

// 全局变量(gojs大量使用该方法创建对象, 仅当前js内部使用)
const $$ = go.GraphObject.make

// 节点上附加的专业属性数据默认值(通风专业相关)
const DefaultNodeData = {
  Name: 'NULL', // 名称
  Remark: '', // 备注
  Density: 1.2, // 空气密度(kg/m^3)
  Level: 0, // 水平(m)
  Power: 0 // 压能(Pa)
}

// 分支上附加的专业属性数据默认值(通风专业相关)
const DefaultLinkData = {
  Name: 'NULL', // 名称
  Remark: '', // 备注
  SectionWidth: 0, // 巷道断面宽度
  SectionHeight: 0, // 巷道断面高度
  Q: 0, // 风量
  V: 0, // 风速
  H: 0, // 压力
  L: 0, // 长度
  RFirc: 0, // 摩擦风阻
  RLocal: 0, // 局部风阻
  RAmend: 0, // 附加风阻
  AvgDenstity: 1.2, // 平均空气密度
  TestQ: 0, // 测试风量
  FixQ: 0, // 固定风量
  R100m: 0, // 百米风阻
  Coff: 0, // 摩擦阻力系数
  BarrierArea: 0, // 构筑物面积
  UpperLimitV: 0, // 风速上限
  LowerLimitV: 0, // 风速下线
  LinkConnectTypes: 2, // 巷道连接类型(参见: vng/property/datas/tunnel-connect-types.js)
  LinkUsingTypes: 0, // 巷道类型(参见: vng/property/datas/tunnel-using-types.js)
  LinkAmendPriority: 0,
  SecTionTypes: 0, // 断面形状(参见: vng/property/datas/section-types.js)
  SupportTypes: 0 // 支护类型(参见: vng/property/datas/support-types.js)
}

// 第2个分支标签支持显示的数据
const AllowShowDataList = {
  Name: '名称',
  L: '长度',
  Q: '风量',
  V: '风速',
  RFirc: '风阻',
  H: '阻力',
  FixQ: '固定风量'
}

// 动画演示过程中允许点击的命令
const AnimationAllowListForCommand = {
  PlayAnimation: 'PlayAnimation',
  UpdateSecondLabel: 'UpdateSecondLabel'
}

// 给gojs节点绑定的所有数据
const Node = {
  // 基本特性
  // 图形类型(固定值)
  type: 'Node',
  // 节点唯一编号
  key: '',
  // id: 0,
  // 节点名称
  text: '',
  // 节点填充色
  // fillColor: config.node.style.fillColor,
  // 颜色(边框和填充共用一个颜色)
  stroke: config.node.style.stroke,
  // 边框线宽
  strokeWidth: config.node.style.strokeWidth,
  // 标签文字大小
  fontSize: config.node.style.fontSize,
  // 标签文字颜色
  fontColor: config.node.style.fontColor,
  // 坐标
  loc: '', // the "loc" property is a string, not a Point object
  // 节点的宽和高
  width: 0,
  height: 0,
  // 节点上附加的专业属性数据
  __datas__: JSON.stringify(DefaultNodeData)
}

// 给gojs分支绑定的所有数据
const Link = {
  // 基本特性
  // 图形类型(固定值)
  type: 'Link',
  // 分支唯一编号
  key: '',
  // id: 0,
  // 分支名称
  text: '',
  // 附加显示的文字(比如风速、风量等),支持换行符
  // text2: '测试\n你好',
  text2: '',
  // 颜色
  stroke: config.edge.style.stroke,
  // 线宽
  strokeWidth: config.edge.style.strokeWidth,
  // 标签文字大小
  fontSize: config.edge.style.fontSize,
  // 标签文字颜色
  fontColor: config.edge.style.fontColor,
  // 始节点key
  from: '',
  // 末节点key
  to: '',
  // 分支形状类型
  category: 'line',
  // arc分支的弧度
  curviness: 0,
  // line分支的中间点坐标
  line_points: '[]',
  // spline分支的控制点坐标
  spline_points: '[]',
  // 是否正交直线
  ortho: false,
  // 分支上附加的专业属性数据
  __datas__: JSON.stringify(DefaultLinkData)
}

// 辅助函数: 构造节点数据
function makeNode(key, name, x, y) {
  const data = {
    ...Node
  }
  // 唯一编号
  data.key = key || makeUUID()
  // data.id = id
  // 名称
  data.text = name
  // 坐标
  data.loc = go.Point.stringify(new go.Point(x, -1 * y))
  // 填充颜色
  // data.fillColor = config.node.style.fillColor
  // 边框颜色
  data.stroke = config.node.style.stroke
  // 边框线宽
  data.strokeWidth = config.node.style.strokeWidth
  // 标签文字颜色
  data.fontColor = config.node.style.fontColor
  // 标签文字大小
  data.fontSize = config.node.style.fontSize
  // 节点宽和高
  data.width = config.node.size[0]
  data.height = config.node.size[1]
  return data
}

// 辅助函数: 构造分支数据
function makeLink(key, from, to, name, category, points, attrs = null) {
  // create a link data from the old node data to the new node data
  const data = {
    ...Link
  }
  // 唯一编号
  data.key = key || makeUUID()
  // data.id = id
  // 名称
  data.text = name
  // 始节点编号
  data.from = from
  // 末节点编号
  data.to = to
  // 分支类型
  data.category = category
  // 线条颜色
  data.stroke = config.edge.style.stroke
  // 线宽
  data.strokeWidth = config.edge.style.strokeWidth
  // 标签文字颜色
  data.fontColor = config.edge.style.fontColor
  // 标签文字大小
  data.fontSize = config.node.style.fontSize
  if (!points) {
    data.category = 'line'
  } else {
    // gojs坐标系的y轴是朝下的
    points.forEach(val => {
      val.y *= -1
    })
    if (category === 'spline' && points.length > 2) {
      // 线特殊处理
      data.spline_points = JSON.stringify(points)
    } else {
      data.line_points = JSON.stringify(points)
      // 正交线段是否合并
      data.ortho = config.layout.splines === 'ortho2'
      // data.corner = 5;
    }
  }
  return data
}
// const core = new Core({parent:xxx})
function Core({ parent }) {
  // 当前操作模式(pointer/node/link)
  this.mode = 'pointer'
  // 当前分支形状类型
  this.itemType = 'pointer'
  // 分支reshape工具(spline)
  this.spline_tool = null
  // 分支reshape工具(arc)
  this.arc_tool = null
  // 显示网格
  this.gridVisible = true
  // 初始gojs
  this.init(parent)
  // 自定义命令id,从1000开始
  this.customCmdId = 1000
  // 自定义命令
  this.customCmds = {}
  // 命令id与名称的反向映射(优化执行效率)
  this.customCmdsIdMap = {}
  // 自定义事件回调函数集合
  this.customEventCallbacks = {}
  // 自定义的异步事件集合
  this.customAsyncEvents = []
  // 自定义的同步事件promise对象
  this.customSyncEventPromise = null
  // 当前显示的字段
  this.secondLabelField = ''
}

Core.prototype.GetDiagram = function() {
  return this.diagram
}

Core.prototype.init = function(parent) {
  this.diagram = $$(go.Diagram, parent, {
    // start everything in the middle of the viewport
    initialContentAlignment: go.Spot.Center,

    // allowZoom: false,
    // allowVerticalScroll: false,
    // allowHorizontalScroll: false,

    'commandHandler.zoomFactor': 1.2,

    // 鼠标滚轮缩放视图
    'toolManager.mouseWheelBehavior': go.ToolManager.WheelZoom,

    // file:///D:/develop/GitTest/GoJS/samples/scrollModes.html
    scrollMode: go.Diagram.InfiniteScroll,

    padding: 0, // scales should be allowed right up against the edges of the viewport

    /* undo/redo开关 */
    'undoManager.isEnabled': true,

    /* 动画效果开关 */
    'animationManager.isEnabled': true,

    // so that the contents and the grid cannot appear too small
    //  参考: D:/develop/GitTest/GoJS/intro/viewport.html#ScrollingModes
    //        D:/develop/GitTest/GoJS/samples/scrollModes.html
    minScale: 0.01,
    maxScale: 5,
    /* 网格设置 */
    'grid.visible': true,
    // 其它网格参数
    // "grid.gridCellSize": new go.Size(30, 20),
    grid: $$(
      go.Panel,
      'Grid',
      $$(go.Shape, 'LineH', {
        stroke: 'gray',
        strokeWidth: 0.5,
        interval: 10
      }),
      // $$(go.Shape, "LineH", { stroke: "darkslategray", strokeWidth: 1, interval: 30 }),
      $$(go.Shape, 'LineV', {
        stroke: 'gray',
        strokeWidth: 0.5,
        interval: 10
      })
      // $$(go.Shape, "LineV", { stroke: "darkslategray", strokeWidth: 1, interval: 30 })
    ),
    'draggingTool.isGridSnapEnabled': true
    // "resizingTool.isGridSnapEnabled": true,

    /* 深度拷贝和删除 */
    // 允许使用ctrl+c、ctrl+v复制粘贴(深度copy)
    // "commandHandler.copiesTree": true,
    // 允许使用delete键删除节点(深度delete)
    // "commandHandler.deletesTree": true,
    // dragging for both move and copy(深度drag)
    // "draggingTool.dragsTree": true,

    /* 最多选择节点个数 */
    // maxSelectionCount: 2
  })

  /* 自定义节点和分支模板 */
  this.configNodeTemplate()
  this.configLinkTemplate()

  // 创建图模型对象

  // 构造model对象,并进行必要的设置
  this.resetModel()
  // diagram.model = new go.GraphLinksModel([], []);

  // 创建缩略图
  // initMinimap()

  // 创建标尺(还不完善!!!)
  // initRuler();

  /* 自定义配置 */
  this.configTools()
  this.configCommands()
  this.configGroup()

  /* 设置事件监听器 */
  this.configListener()

  // 动画对象
  this.animation = null
}

Core.prototype.configNodeTemplate = function() {
  // define the Node template
  this.diagram.nodeTemplate = BuildEllipseTemplate()
}

Core.prototype.configLinkTemplate = function() {
  // replace the default Link template in the linkTemplateMap

  // 有动画效果的分支模板
  this.diagram.linkTemplateMap.add('spline', BuildSplineTemplate())
  this.diagram.linkTemplateMap.add('arc', BuildArcTemplate())
  this.diagram.linkTemplateMap.add('line', BuildLineTemplate())
  // 无动画效果的分支模板
  this.diagram.linkTemplateMap.add(
    'animate-spline',
    BuildAnimateSplineTemplate()
  )
  this.diagram.linkTemplateMap.add('animate-arc', BuildAnimateArcTemplate())
  this.diagram.linkTemplateMap.add('animate-line', BuildAnimateLineTemplate())
}

Core.prototype.configTools = function() {
  const that = this
  // 修改平移视图默认快捷键(按住鼠标左键 --> ctrl+鼠标左键)
  this.diagram.toolManager.panningTool.canStart = function() {
    // Maybe do something else before
    // ...

    // Be careful about using 'this' within such functions!

    // In cases where you want normal behavior, call the base functionality.
    // Note the reference to the prototype
    // and the call to 'call' passing it what 'this' should be.

    // 默认左键拖动视图
    // return go.PanningTool.prototype.canStart.call(diagram.toolManager);
    // ctrl+鼠标左键
    // return diagram.lastInput.control && diagram.lastInput.left;
    // 鼠标中键(有bug,按住中键可以拖动视图,但是滚轮滚动却失效了)
    if (that.diagram.lastInput.middle) {
      // gojs的鼠标中键按下之后也会出发滚动scroll消息,导致滚轮缩放功能失效
      // 需要强制修改mouseWheelBehavior属性!!!
      that.diagram.toolManager.mouseWheelBehavior = go.ToolManager.WheelZoom
      return true
    } else {
      return false
    }
    // Maybe do something else after
    // ...
  }

  // 修改拖拽框选的外框效果
  this.diagram.toolManager.dragSelectingTool.isPartialInclusion = false // 必须全部包含在框里才能选中!
  // replace the magenta box with a red one
  this.diagram.toolManager.dragSelectingTool.box = $$(
    go.Part,
    {
      layerName: 'Tool',
      selectable: false
    },
    $$(go.Shape, {
      name: 'SELECT_SHAPE',
      fill: 'rgba(194,218,255,0.6)',
      stroke: '#101010',
      strokeWidth: 0.5
    })
  )

  this.diagram.allowLink = false // linking is only started via buttons, not modelessly
  this.diagram.toolManager.linkingTool.portGravity = 0 // no snapping while drawing new links
  // this.diagram.toolManager.linkingTool.findLinkablePort = function() {
  //   if (that.isAnimating()) {
  //     MessageBox.alert('正在演示动画, 不允许执行绘图操作!', '提示')
  //     return null
  //   } else {
  //     return go.LinkingTool.prototype.findLinkablePort.call(this)
  //   }
  // }

  // this.diagram.toolManager.linkingTool.canStart = function() {
  //   if (that.isAnimating()) {
  //     // MessageBox.alert('正在演示动画, 不允许执行绘图操作!', '提示')
  //     return false
  //   } else {
  //     return go.LinkingTool.prototype.canStart.call(this)
  //   }
  // }

  this.diagram.toolManager.linkingTool.doActivate = function() {
    // change the curve of the LinkingTool.temporaryLink
    // console.log(this.itemType)
    this.temporaryLink.curve =
      !this.itemType || this.itemType === 'line'
        ? go.Link.Normal
        : go.Link.Bezier
    this.temporaryLink.path.stroke = 'red' // 红色
    this.temporaryLink.path.strokeWidth = config.edge.style.strokeWidth
    go.LinkingTool.prototype.doActivate.call(this)
  }

  // 自定义分支创建过程
  // override the link creation process
  this.diagram.toolManager.linkingTool.insertLink = function(
    fromnode,
    fromport,
    tonode,
    toport
  ) {
    // to control what kind of Link is created,
    // change the LinkingTool.archetypeLinkData's category
    // diagram.model.setCategoryForLinkData(this.archetypeLinkData, 'line');
    // customize the data for the new node
    // console.log(fromnode, tonode)
    const newId = that.getLinkCount() + 1
    this.archetypeLinkData = makeLink(
      null,
      fromnode.key,
      tonode.key,
      'e' + newId,
      'line',
      null,
      null
    )

    return go.LinkingTool.prototype.insertLink.call(
      this,
      fromnode,
      fromport,
      tonode,
      toport
    )
  }

  this.spline_tool = this.diagram.toolManager.linkReshapingTool
  this.arc_tool = new CurvedLinkReshapingTool()

  // 允许在画布上面双击的时候创建节点
  // 自定义节点插入过程
  this.diagram.toolManager.clickCreatingTool.archetypeNodeData = {} // enable ClickCreatingTool
  this.diagram.toolManager.clickCreatingTool.isDoubleClick = true // operates on a single click in background
  this.diagram.toolManager.clickCreatingTool.canStart = function() {
    return (
      that.mode === 'node' && go.ClickCreatingTool.prototype.canStart.call(this)
    )
    // const ret = go.ClickCreatingTool.prototype.canStart.call(this)
    // if (ret) {
    //   if (that.isAnimating()) {
    //     MessageBox.alert('正在演示动画, 不允许执行绘图操作!', '提示')
    //     return false
    //   } else {
    //     // but only in "node" creation mode
    //     return that.mode === 'node'
    //   }
    // } else {
    //   return ret
    // }
  }
  this.diagram.toolManager.clickCreatingTool.insertPart = function(loc) {
    // customize the data for the new node
    // 当前节点个数作为id
    const newId = that.getNodeCount() + 1
    // console.log(loc)
    this.archetypeNodeData = makeNode(null, 'v' + newId, loc.x, loc.y)
    return go.ClickCreatingTool.prototype.insertPart.call(this, loc)
  }
  // install the LinkLabelDraggingTool as a "mouse move" tool
  this.diagram.toolManager.mouseMoveTools.insertAt(
    0,
    new LinkLabelDraggingTool()
  )
}

Core.prototype.configCommands = function() {
  // 自定义复制粘贴命令以及键盘方向键控制(默认粘贴时偏移20)
  this.diagram.commandHandler = new DrawCommandHandler(30, 0) // defined in DrawCommandHandler.js
  // 通过方向键可以移动节点位置
  this.diagram.commandHandler.arrowKeyBehavior = 'move'
  //   diagram.commandHandler.arrowKeyBehavior = "select";
  //   diagram.commandHandler.arrowKeyBehavior = "scroll";

  // this.diagram.commandHandler.canDeleteSelection = function () {

  // }
  const that = this
  this.diagram.commandHandler.groupSelection = function() {
    MessageBox.prompt('请输入分组的名称', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    })
      .then(({ value }) => {
        // 设置输入的分组名称
        that.diagram.commandHandler.archetypeGroupData.text = value
        // 调用分组功能
        go.CommandHandler.prototype.groupSelection.call(
          that.diagram.commandHandler
        )
      })
      .catch(() => {
        // this.$message({
        //   type: 'info',
        //   message: '取消输入'
        // })
      })
  }
}

Core.prototype.resize = function(width, height) {
  this.diagram.requestUpdate()
  // 图形整体居中
  // this.diagram.scrollToRect(this.diagram.documentBounds)
}

Core.prototype.update = function() {
  this.diagram.rebuildParts()
}

Core.prototype.resetModel = function() {
  this.diagram.model = new go.GraphLinksModel([], [])
  // 要保证findLinkDataForKey()有效, 必须设置linkKeyProperty
  // 参考: https://gojs.net/latest/api/symbols/GraphLinksModel.html
  this.diagram.model.linkKeyProperty = 'key'
}

Core.prototype.getNodeCount = function() {
  return this.diagram.nodes.count
}

Core.prototype.getLinkCount = function() {
  return this.diagram.links.count
}

Core.prototype.setNodeCursor = function(cursor, allowLink) {
  this.diagram.startTransaction('Core.setNodeCursor')
  // 是否允许link操作
  this.diagram.allowLink = allowLink
  // 设置link时的节点上显示哪种鼠标
  this.diagram.nodes.each(n => {
    n.port.cursor = cursor
  })
  this.diagram.commitTransaction('Core.setNodeCursor')
}

Core.prototype.setMode = function(mode) {
  if (this.isAnimating() && mode !== 'pointer') {
    MessageBox.alert('正在演示动画, 不允许执行绘图操作!', '提示')
    return false
  }
  // 修改鼠标模式
  this.mode = mode
  if (mode === 'pointer') {
    this.itemType = 'pointer'
    this.setNodeCursor('', false)
  } else if (mode === 'node') {
    this.itemType = 'node'
    this.setNodeCursor('', false)
  } else if (mode === 'link') {
    this.itemType = 'line'
    this.setNodeCursor('pointer', true)
  }
  return true
}

Core.prototype.configListener = function() {
  this.diagram.addDiagramListener('ChangedSelection', e => {
    const parts = []
    e.diagram.selection.each(part => {
      parts.push(part)
      // console.log('选中:', part.data)
    })
    // 更新选择集
    Store.UpdateSelection(parts)
  })

  // diagram.addDiagramListener("SelectionMoved", function (e) {
  //     // const div = diagram.div;
  //     // div.style.width = '200px';
  //     // diagram.requestUpdate(); // Needed!
  //     console.log(e.subject);
  //     const it = e.subject.iterator;
  //     while (it.next()) {
  //         console.log(it.value);
  //         // const pt = it.value;
  //         // points.push({ x: pt.x, y: -1 * pt.y });
  //     }
  // });

  // diagram.addDiagramListener("ObjectSingleClicked", function (e) {
  //     // const div = diagram.div;
  //     // div.style.width = '200px';
  //     // diagram.requestUpdate(); // Needed!
  //     const part = e.subject.part;
  //     // console.log(part.data);
  //     const data = part.data;
  //     // console.log(part.type);
  // });

  // diagram.addDiagramListener("ObjectDoubleClicked", function (e) {
  //     const part = e.subject.part;
  //     console.log("Double-clicked at " + part.data.key);
  // });

  this.diagram.addDiagramListener('ClipboardPasted', e => {
    const parts = e.subject
    parts.each(part => {
      // console.log(part.type);
      // 修改复制粘贴节点或分支的key
      if (Util.Go.partIsNode(part)) {
        e.diagram.model.setKeyForNodeData(part.data, makeUUID())
      } else if (Util.Go.partIsLink(part)) {
        e.diagram.model.setKeyForLinkData(part.data, makeUUID())
      }
      // 修改节点或分支的text属性值
      e.diagram.model.setDataProperty(
        part.data,
        'text',
        part.data.text + '-副本'
      )
    })
    // console.log('Pasted ' + e.diagram.selection.count + ' parts')
  })

  this.diagram.addDiagramListener('ViewportBoundsChanged', e => {
    // old value
    // const scale = e.subject.scale
    // const rect = e.parameter
    // console.log('旧的scale:', scale)
    // new value
    const newScale = Math.round(e.diagram.scale * 100)
    Store.UpdateScale(newScale)
    // console.log(newScale);
    // console.log(diagram.scale);
    // console.log(diagram.minScale);
    // console.log(diagram.maxScale);
    // console.log(diagram.scrollMode);
  })

  // generate unique label for valve on newly-created flow link
  // diagram.addDiagramListener("LinkDrawn", function (e) {
  //     const link = e.subject;
  //     if (link.category === "line") {
  //         diagram.startTransaction("updateNode");
  //         const newNodeId = "v" + getNodeCount();
  //         const labelNode = link.labelNodes.first();
  //         diagram.model.setDataProperty(labelNode.data, "label", newNodeId);
  //         diagram.commitTransaction("updateNode");
  //     }
  // });
}

Core.prototype.configGroup = function() {
  // 允许分组(Ctrl+G)
  this.diagram.commandHandler.archetypeGroupData = {
    text: 'Group',
    isGroup: true,
    color: 'blue'
  }
  // 允许取消分组(Ctrl+Shift+G)
  this.diagram.groupTemplate.ungroupable = true
  // 分组的外观效果模板
  this.diagram.groupTemplate = $$(
    go.Group,
    'Auto',
    // enable Ctrl-Shift-G to ungroup a selected Group
    { selectionObjectName: 'PANEL', ungroupable: true },
    $$(
      go.Shape,
      'RoundedRectangle', // surrounds everything
      {
        fill: 'rgba(128,128,128,0.2)',
        stroke: 'skyblue',
        strokeWidth: 3,
        portId: '',
        // cursor: "pointer", // the Shape is the port, not the whole Node
        // allow all kinds of links from and to this port
        fromLinkable: false,
        toLinkable: false
      }
    ),
    $$(
      go.Panel,
      'Vertical', // position header above the subgraph
      { defaultAlignment: go.Spot.Left },
      $$(
        go.Panel,
        'Horizontal', // the header
        { defaultAlignment: go.Spot.Top },
        $$('SubGraphExpanderButton'), // this Panel acts as a Button
        $$(
          go.TextBlock, // group title near top, next to button
          {
            font: makeFont(config.node.style.fontSize, true),
            isMultiline: true,
            editable: false
          },
          new go.Binding('text', 'text').makeTwoWay(),
          new go.Binding('font', 'fontSize', Util.Go.toFont).makeTwoWay(
            Util.Go.fromFont
          )
        )
      ),
      $$(go.Placeholder, { margin: 5, background: 'transparent' })
    )
  )
}

Core.prototype.addNode = function(key, name, x, y) {
  const node_data = makeNode(key, name, x, y)
  // add the new node data to the model
  this.diagram.model.addNodeData(node_data)
  return node_data.key
}

Core.prototype.addLink = function(
  key,
  from,
  to,
  name,
  category,
  points,
  attrs
) {
  // 生成分支数据
  const link_data = makeLink(key, from, to, name, category, points, attrs)
  // and add the link data to the model
  this.diagram.model.addLinkData(link_data)
  return link_data.key
}

Core.prototype.findNodeByKey = function(key) {
  return this.diagram.findNodeForKey(key)
}

Core.prototype.findLinkByKey = function(key) {
  return this.diagram.findLinkForKey(key)
}

Core.prototype.getParamObject = function(sType) {
  if (!sType) {
    return null
  } else if (sType === 'Node') {
    return Node
  } else if (sType === 'Link') {
    return Link
  } else {
    return null
  }
}

Core.prototype.getDataObject = function(sType) {
  if (!sType) {
    return null
  } else if (sType === 'Node') {
    return DefaultNodeData
  } else if (sType === 'Link') {
    return DefaultLinkData
  } else {
    return null
  }
}

Core.prototype.createNetwork = function(data) {
  this.resetModel()
  // if (data == null || data.code == 1) {
  //   layer.alert('导入失败', { skin: 'layui-layer-moon' })
  //   // this.diagram.rollbackTransaction("importNetworkCallBack");
  //   return
  // }
  this.diagram.startTransaction('Core.create')

  // console.log(data)
  const nodes_map = {}
  for (const i in data.nodes) {
    const node = data.nodes[i]
    nodes_map[node.id] = this.addNode(null, 'v' + node.id, node.x, node.y)
    // console.log('添加节点:', nodes_map[node.id], node.id, node.x, node.y)
  }
  for (const i in data.edges) {
    const edge = data.edges[i]
    // console.log(edge)
    let ctrl_points = edge.ctrl_points

    // 处理特殊情况
    if (!ctrl_points) {
      ctrl_points = []
    }
    const from = nodes_map[edge.s]
    const to = nodes_map[edge.t]
    if (ctrl_points.length > 2 && config.layout.splines === 'spline') {
      // 添加曲线分支
      this.addLink(null, from, to, 'e' + edge.id, 'spline', ctrl_points, edge)
    } else {
      // 添加直线分支
      this.addLink(null, from, to, 'e' + edge.id, 'line', ctrl_points, edge)
    }
  }

  this.diagram.commitTransaction('Core.create')
}

Core.prototype.updateNetwork = function(data) {
  this.diagram.startTransaction('Core.updateNetwork')

  // console.log(data);
  for (const i in data.nodes) {
    const node = data.nodes[i]
    // console.log(node);
    const node_data = this.diagram.model.findNodeDataForKey(node.id)
    if (node_data == null) continue

    this.diagram.model.setDataProperty(
      node_data,
      'loc',
      go.Point.stringify(new go.Point(node.x, -1 * node.y))
    )
  }

  for (const i in data.edges) {
    const edge = data.edges[i]
    // console.log(edge);
    // 查找分支对象
    // 使用findLinkDataForKey()方法必须保证linkKeyProperty已设置!!!
    const edge_data = this.diagram.model.findLinkDataForKey(edge.id)
    // console.log(edge_data);
    if (!edge_data) continue

    let ctrl_points = edge.ctrl_points
    // 处理特殊情况
    if (!ctrl_points) {
      ctrl_points = []
    }
    let link_data = {}
    if (ctrl_points.length > 2 && config.layout.splines === 'spline') {
      // 生成曲线分支数据
      link_data = makeLink(
        edge_data.key,
        // edge_data.id,
        edge_data.from,
        edge_data.to,
        'e' + edge_data.id,
        'spline',
        ctrl_points,
        edge_data
      )
    } else {
      // 生成直线分支数据
      link_data = makeLink(
        edge_data.key,
        // edge_data.id,
        edge_data.from,
        edge_data.to,
        'e' + edge_data.id,
        'line',
        ctrl_points,
        edge_data
      )
    }
    // console.log(link_data);
    // 更新分支的坐标和形状数据
    for (const key in link_data) {
      if (
        key === 'category' ||
        key === 'line_points' ||
        key === 'spline_points' ||
        key === 'ortho'
      ) {
        this.diagram.model.setDataProperty(edge_data, key, link_data[key])
      }
    }
  }
  this.diagram.commitTransaction('Core.updateNetwork')
}

Core.prototype.readFromJson = function(data) {
  if (!data) return

  // 根据计算的结果更新diagram
  // 导入网络图模型数据(直接修改model,不能使用事务!!!)
  this.diagram.model = go.Model.fromJson(data)
  // 覆盖全局配置对象
  if (data.hasOwnProperty('config')) {
    for (const key in config) {
      if (data.hasOwnProperty(key)) {
        config[key] = data[key]
      }
    }
  }
}

Core.prototype.getLastNode = function() {
  const nodes = this.diagram.model.nodeDataArray
  return this.diagram.findNodeForData(nodes[nodes.length - 1])
}

Core.prototype.fitToNode = function(node) {
  if (node != null) {
    this.diagram.select(node)
    // if the new node is off-screen, scroll the diagram to show the new node
    // this.diagram.scrollToRect(node.actualBounds);
  }
}

Core.prototype.reverseLinks = function(parts) {
  const that = this
  this.diagram.startTransaction('reverse_link')
  // 遍历分支集合
  parts.forEach(part => {
    const data = part.data
    // console.log(data);
    const from = data.from
    const to = data.to
    that.diagram.model.setFromKeyForLinkData(data, to)
    that.diagram.model.setToKeyForLinkData(data, from)
  })
  this.diagram.commitTransaction('reverse_link')
}

Core.prototype.getAllLinks = function() {
  const parts = this.diagram.links
  if (parts === null || parts.count === 0) {
    return null
  }
  const datas = []
  parts.each(part => {
    if (Util.Go.partIsLink(part)) {
      datas.push(part)
    }
  })
  return datas
}

Core.prototype.currentSelect = function() {
  const parts = []
  this.diagram.selection.each(part => {
    parts.push(part)
  })
  return parts
}

Core.prototype.selectParts = function(parts, type, reverse = false) {
  // 从选择集中过滤掉非指定type的图形
  const noUseParts = Util.Go.filterParts(parts, type, reverse)

  this.diagram.startTransaction('Core.selectParts')
  noUseParts.forEach(part => {
    // 取消选中
    part.isSelected = !reverse
  })
  this.diagram.commitTransaction('Core.selectParts')
  // 更新选择集
  Store.UpdateSelection(this.currentSelect())
}

Core.prototype.getSelectedLinks = function() {
  return Util.Go.filterParts(this.currentSelect(), 'Link')
}

Core.prototype.setLinkCategory = function(parts, category) {
  const links = Util.Go.filterParts(parts, 'Link')
  if (!links) return
  this.diagram.startTransaction('Core.setLinkCategory')
  // 遍历分支集合
  links.forEach(link => {
    // console.log('xxxxxxxxxx:', data, category)
    const e = this.diagram.findLinkForData(link.data)
    if (e != null) {
      this.diagram.model.setCategoryForLinkData(e, category)
    }
  })
  this.diagram.commitTransaction('Core.setLinkCategory')

  if (category === 'spline') {
    this.diagram.toolManager.linkReshapingTool = this.spline_tool
  } else if (category === 'arc') {
    this.diagram.toolManager.linkReshapingTool = this.arc_tool
  } else {
    this.diagram.toolManager.linkReshapingTool = null
  }
  // this.diagram.rebuildParts()
}

// 生成svg图形
// 取出dom对象的html字符串源码: svg.outerHTML
Core.prototype.exportSvg = function(_scale) {
  // 生成svg
  return this.diagram.makeSvg({
    scale: _scale
  })
}

Core.prototype.exportImg = function(_type, _scale) {
  const bound = this.diagram.documentBounds
  console.log(bound)

  const options = {
    scale: _scale,
    // 参考: https://gojs.net/latest/intro/makingImages.html
    // The default value for maxSize is go.Size(2000, 2000)
    maxSize: new go.Size(bound.width * _scale, bound.height * _scale)
  }
  // 设置颜色
  // png图形支持rgba格式,默认为透明; 而jpeg格式只支持rgb,不支持透明背景
  // options['background'] = "rgba(255, 255, 255, 0)";
  if (_type === 'jpeg') {
    options['type'] = 'image/jpeg'
    options['background'] = 'AntiqueWhite' // 不设置该项,默认为透明背景
  }

  // 生成jpg
  return this.diagram.makeImage(options)
}

Core.prototype.exportJson = function() {
  // 使用内置的toJson方法导出模型数据
  const model_json = this.diagram.model.toJson()
  const graph = JSON.parse(model_json)
  // 附加全局配置数据
  graph['config'] = config
  return graph
}

// Core.prototype.exportArray = function() {
//   const graph = this.exportTopology()
//   // if (graph == null) {
//   //   layer.alert('生成通风网络拓扑数据失败!', { skin: 'layui-layer-moon' })
//   //   return
//   // }
//   // console.log(graph)
//   // 利用js生成字符串(效率可能不高!)
//   const datas = []
//   for (const i in graph['edges']) {
//     const obj = graph['edges'][i]
//     const s = [obj.id, obj.s, obj.t, obj.r, obj.q]
//     datas.push(s.join('\t'))
//   }
//   return datas
// }

Core.prototype.exportGraphDatas = function(ready_export) {
  if (this.diagram.model.linkDataArray.length === 0) {
    return null
  }

  const that = this
  const node_datas = []
  // 遍历节点
  this.diagram.model.nodeDataArray.forEach(data => {
    // console.log(data);
    // const pt = go.Point.parse(data.loc);
    // 获取节点的几何对象
    const node = that.diagram.findNodeForData(data)
    // console.log(node);
    const rect = node.actualBounds
    // 节点的几何信息
    const obj = {
      id: data.key,
      name: data.text,
      // width: rect.width,
      // height: rect.height,
      width: data.width,
      height: data.height
    }
    let fontsize = data.fontSize
    if (fontsize <= 0) fontsize = config.node.style.fontSize
    obj['fontsize'] = fontsize

    if (ready_export) {
      obj['x'] = rect.centerX
      obj['y'] = -1 * rect.centerY
    }
    node_datas.push(obj)
  })

  const links = []
  const link_datas = []
  // 遍历分支
  this.diagram.model.linkDataArray.forEach(data => {
    // 分支和节点的拓扑信息
    // console.log(data);
    links.push({
      id: data.key,
      s: data.from,
      t: data.to
    })

    // 分支的几何信息
    const link = that.diagram.findLinkForData(data)
    let points = []
    if (data.category === 'line') {
      points = JSON.parse(data.line_points)
    } else if (data.category === 'spline') {
      points = JSON.parse(data.spline_points)
    }
    points.forEach(val => {
      val.y *= -1
    })
    // console.log(link.curviness);
    const tpt = link.midPoint
    tpt.y *= -1
    // console.log(tpt);
    let curviness = data.curviness
    if (isNaN(curviness)) curviness = 0

    const obj = {
      id: data.key,
      name: data.text
    }
    let fontsize = data.fontSize
    if (fontsize <= 0) fontsize = config.edge.style.fontSize
    obj['fontsize'] = fontsize

    if (ready_export) {
      obj['category'] = data.category
      obj['curviness'] = curviness
      obj['points'] = JSON.stringify(points)
      obj['midPoint'] = go.Point.stringify(tpt)
    }
    link_datas.push(obj)
  })
  return {
    edges: links,
    node_weights: node_datas,
    edge_weights: link_datas
  }
}

Core.prototype.exportTopology = function() {
  if (this.diagram.model.linkDataArray.length === 0) {
    return null
  }

  const node_datas = []
  // 遍历节点
  this.diagram.model.nodeDataArray.forEach(data => {
    // console.log(data);
    // 获取节点的几何对象
    // const node = this.diagram.findNodeForData(data);
    // 节点的几何信息
    node_datas.push({
      id: data.id
      // p: data.p, // 节点压力
      // z: data.z // 节点坐标
    })
  })
  const link_datas = []
  // 遍历分支
  this.diagram.model.linkDataArray.forEach(data => {
    // 分支和节点的拓扑信息
    // console.log(data);
    const u = this.diagram.model.findNodeDataForKey(data.from)
    const v = this.diagram.model.findNodeDataForKey(data.to)
    link_datas.push({
      id: data.id,
      s: u.id,
      t: v.id
      // r: data.r, // 风阻
      // q: data.q, // 风量
      // area: data.area // 断面积
    })
  })
  return { edges: link_datas, nodes: node_datas }
}

Core.prototype.regCommand = function(name, func, id = 0) {
  if (func === null) {
    console.log('注册自定义命令:', name, '失败,原因:未指定处理函数')
    return
  }
  if (id === null || id <= 0) {
    // 通过时间戳生成 UUID(数字范围太大,js报错)
    // id = Math.round(new Date().getTime());
    // 从id数组中随机取出一个数(效率问题)
    // id = util.array.randomGetOne(this.idArray);
    // 自动分配id即可,不需要用随机数!!!
    id = this.customCmdId
    this.customCmdId = this.customCmdId + 1
  }
  const uName = name.toUpperCase()
  if (this.customCmds.hasOwnProperty(name)) {
    // 删除原来的id --> name映射
    delete this.customCmdsIdMap[this.customCmds[uName].id]
    this.customCmds[uName].id = id
    this.customCmds[uName].func = func
  } else {
    this.customCmds[uName] = {
      id: id,
      func: func
    }
  }
  // 映射id --> name
  this.customCmdsIdMap[id] = uName
}

Core.prototype.hasCommand = function(name) {
  return this.customCmds.hasOwnProperty(name.toUpperCase())
}

Core.prototype.getCommandFunc = function(name) {
  const uName = name.toUpperCase()
  if (this.hasCommand(uName)) {
    return this.customCmds[uName].func
  } else {
    return null
  }
}

Core.prototype.getCommandIDByName = function(name) {
  const uName = name.toUpperCase()
  if (this.hasCommand(uName)) {
    return this.customCmds[uName].id
  } else {
    return 0
  }
}

Core.prototype.getCommandNameByID = function(iCmd) {
  if (this.customCmdsIdMap.hasOwnProperty(iCmd)) {
    return this.customCmdsIdMap[iCmd]
  } else {
    return ''
  }
}

Core.prototype.fastRegCommand = function(Commandconfig) {
  // console.log('--> call fastRegCommand')
  for (const name in Commandconfig) {
    const value = Commandconfig[name]
    let func = null
    let id = 0
    if (isObject(value)) {
      func = value.func || null
      id = value.id || 0
    } else if (isFunction(value)) {
      func = value
    }
    // 注册自定义命令
    this.regCommand(name, func, id)
  }
}

// 以函数方式调用
Core.prototype.CallCommandAsFunction = function(name, args = null) {
  if (
    this.isAnimating() &&
    !AnimationAllowListForCommand.hasOwnProperty(name)
  ) {
    MessageBox.alert('正在演示动画, 请点击"动画演示"按钮停止动画!', '提示')
    return
  }
  // 调用自定义命令函数
  const func = this.getCommandFunc(name)
  // console.log('xxxxx:', name, func, this.customCmds)
  if (func !== null) {
    // 执行命令对应的函数(并附加参数args)
    func(args)
  }
}

Core.prototype.fastRegCustomEventFunc = function(CustomEventconfig) {
  // console.log('--> call FastRegCustomEventFunc')
  if (isObject(CustomEventconfig)) {
    this.customEventCallbacks = CustomEventconfig
  }
}

// 发送自定义事件
Core.prototype.postEvent = function(name, args) {
  const ret = { code: false }
  try {
    const keys = name.split('/')
    if (keys === null || keys.length === 0) {
      throw new Error('事件name参数不合法:' + name)
    }

    // console.log("发送自定义消息:", keys, args);
    // 分解json对象(如果格式不正确,就报错)
    let func = this.customEventCallbacks
    let i = 0
    keys.forEach(_name => {
      if (isObject(func) && func.hasOwnProperty(_name)) {
        func = func[_name]
        i = i + 1
      }
    })
    // console.log('[开始]事件:', name)
    // 调用函数
    if (i === keys.length && isFunction(func)) {
      // 同步执行
      ret.code = true
      ret.data = func(args)
    } else {
      // throw new Error(name + '事件处理函数未定义')
      ret.code = true
      ret.data = '处理函数未定义'
    }
    // console.log('[结束]事件:', name, '参数:', args, '返回结果:', ret)
  } catch (err) {
    console.log('[ERROR]postEvent:', err)
    ret.code = false
    ret.err = err
  }
  return ret
}

// 异步发送事件
Core.prototype.postAsyncEvent = function(deliverer, name, args) {
  // console.log(
  //   '[postAsyncEvent]来源:',
  //   deliverer,
  //   '消息名称:',
  //   name,
  //   '消息参数:',
  //   args
  // )
  const that = this
  this.customAsyncEvents.push(
    new Promise(function(resolve, reject) {
      const ret = that.postEvent(name, args)
      // resolve(ret)
      if (!ret.code) {
        reject(ret.err)
      } else {
        resolve(ret.data)
      }
    })
  )
  // console.log('postAsyncEvent,个数:', this.customAsyncEvents.length)
  return this
}

// 批量发送与图元相关的异步事件(新增、删除、修改)
Core.prototype.postAsyncEntityEvents = function(
  deliverer,
  parts,
  sEventName,
  args = null
) {
  if (args === undefined || args === null) {
    args = {}
  }
  const that = this
  parts.forEach(part => {
    // console.log('[postAsyncEntityEvents]', '图元:', part, '事件:', sEventName)
    that.postAsyncEvent(
      deliverer,
      BUILD_ENTITY_EVENT_NAME(that.getType(part), sEventName),
      {
        core: that,
        part: part, // 图元对象
        ...args // 附加参数(使用es6解构语法)
      }
    )
  })
  return this
}

// 简写
Core.prototype.postAsyncEntityEventsNow = function(
  deliverer,
  parts,
  sEventName,
  args = null
) {
  this.postAsyncEntityEvents(deliverer, parts, sEventName, args).finishEvent()
}

// 发送同步事件
Core.prototype.postSyncEvent = function(deliverer, name, args) {
  const that = this
  const newPromise = data => {
    // console.log(data)
    return new Promise(function(resolve, reject) {
      const ret = that.postEvent(name, args)
      if (!ret.code) {
        reject(ret.err)
      } else {
        resolve(ret.data)
      }
    })
  }
  if (this.customSyncEventPromise === null) {
    this.customSyncEventPromise = newPromise(null)
  } else {
    // 使用then进行链式调用(按顺序一个一个的执行事件)
    this.customSyncEventPromise.then(newPromise)
  }
  return this
}

// 批量发送与图元相关的同步事件(新增、删除、修改)
Core.prototype.postSyncEntityEvents = function(
  deliverer,
  parts,
  sEventName,
  args
) {
  if (args === undefined || args === null) {
    args = {}
  }
  const that = this
  parts.forEach(part => {
    that.postSyncEvent(
      deliverer,
      BUILD_ENTITY_EVENT_NAME(that.getType(part), sEventName),
      {
        core: that,
        part: part, // 图元对象
        ...args // 附加参数(使用es6解构语法)
      }
    )
  })
  return this
}

// 事件终点
Core.prototype.finishEvent = function(success = null, error = null) {
  if (this.customAsyncEvents.length > 0) {
    // 以并行的方式同时触发异步事件
    Promise.all(this.customAsyncEvents)
      .then(results => {
        // 执行用户指定的回调函数
        if (isFunction(success)) {
          success(results)
        }
        // console.log('[结果]异步事件:', results)
      })
      .catch(err => {
        if (isFunction(error)) {
          error(err)
          console.log('[异常]异步事件:', err)
        }
      })
  } else {
    // 以串行的方式按照顺序执行
    if (this.customSyncEventPromise !== null) {
      this.customSyncEventPromise
        .then(data => {
          if (isFunction(success)) {
            success(data)
          }
        })
        .catch(err => {
          if (isFunction(error)) {
            error(err)
            console.log('[异常]异步事件:', err)
          }
        })
    }
    this.customSyncEventPromise = null
  }
  // 快速清空数组
  this.customAsyncEvents.length = 0
}

Core.prototype.undo = function() {
  this.diagram.undoManager.undo()
}

Core.prototype.redo = function() {
  this.diagram.undoManager.redo()
}

Core.prototype.group = function() {
  this.diagram.commandHandler.groupSelection()
}

Core.prototype.unGroup = function() {
  this.diagram.commandHandler.ungroupSelection()
}

Core.prototype.copy = function() {
  this.diagram.commandHandler.copySelection()
}
Core.prototype.cut = function() {
  this.diagram.commandHandler.cutSelection()
}
Core.prototype.paste = function() {
  this.diagram.commandHandler.pasteSelection()
}

Core.prototype.delete = function() {
  this.diagram.commandHandler.deleteSelection()
}

Core.prototype.getSelectionCount = function() {
  return this.diagram.selection.count
}

Core.prototype.selectAll = function() {
  this.diagram.commandHandler.selectAll()
}

Core.prototype.zoomIn = function() {
  this.diagram.commandHandler.increaseZoom()
}

Core.prototype.zoomOut = function() {
  this.diagram.commandHandler.decreaseZoom()
}

Core.prototype.zoomTo = function(sType, key) {
  let part = null
  if (sType === 'Node') {
    part = this.findNodeByKey(key)
  } else if (sType === 'Link') {
    part = this.findLinkByKey(key)
  }
  if (part) {
    // 清空选择集
    this.diagram.clearSelection()
    // 定位到图形
    this.diagram.commandHandler.scrollToPart(part)
    // 选中
    this.diagram.select(part)
  }
}

Core.prototype.calScale = function() {
  // 计算新的比例
  const db = this.diagram.documentBounds
  const vb = this.diagram.viewportBounds
  let scale = Math.min(vb.width / db.width, vb.height / db.height)
  if (isNaN(scale) || scale === Infinity || scale === -Infinity) {
    scale = 1.0
  }
  // console.log(
  //   'zoom fit:',
  //   scale,
  //   'db:',
  //   db.width,
  //   db.width,
  //   'vb:',
  //   vb.height,
  //   vb.height
  // )
  return scale
}

Core.prototype.zoomFit = function() {
  // 图形整体居中
  this.diagram.centerRect(this.diagram.documentBounds)
  // 缩放到1.0比例尺
  this.diagram.commandHandler.zoomToFit()
  // 计算新的比例
  const scale = Math.floor(this.calScale() * 100) - 2
  // console.log('旧比例:', this.getScale(), '新比例:', scale)
  this.setScale(scale)

  // 更新scale变量
  // store.UpdateScale(scale)
}

Core.prototype.getScale = function() {
  return Math.round(this.diagram.scale * 100)
}

Core.prototype.setScale = function(val) {
  // this.diagram.startTransaction('Core.setScale')
  // // console.log('set scale:', val)
  // this.diagram.scale = val * 0.01
  // this.diagram.commitTransaction('Core.setScale')
  this.diagram.commandHandler.resetZoom(val * 0.01)
}

Core.prototype.showGrid = function(visible) {
  this.diagram.startTransaction('Core.toggleGrid')
  this.diagram.grid.visible = visible
  this.diagram.commitTransaction('Core.toggleGrid')
}

Core.prototype.release = function() {
  // 清除所有注册命令
  clearObject(this.customCmdsIdMap)
  this.customCmdsIdMap = null
  for (const name in this.customCmds) {
    this.customCmds[name].func = null
  }
  clearObject(this.customCmds)
}

Core.prototype.getType = function(part) {
  if (!part || !part.data) {
    return null
  } else if (Util.Go.partIsNode(part)) {
    return 'Node'
  } else if (Util.Go.partIsLink(part)) {
    return 'Link'
  } else {
    return null
  }
}

Core.prototype.hasParam = function(part, field) {
  if (!part || !part.data) return false
  const obj = this.getParamObject(this.getType(part))
  return obj && obj.hasOwnProperty(field)
}

Core.prototype.getParamSet = function(part, args) {
  if (!part || !part.data) return null
  const obj = this.getParamObject(this.getType(part))
  if (!obj) return null

  const s = new Set()
  for (const field in args) {
    if (obj.hasOwnProperty(field)) {
      s.add(field)
    }
  }
  return s
}

Core.prototype.getParam1 = function(part, field) {
  if (!part || !part.data) return null
  const sType = this.getType(part)
  const obj = this.getParamObject(sType)
  if (!obj || !obj.hasOwnProperty(field)) return null
  return part.data[field]
}

Core.prototype.getParam2 = function(part, fields) {
  if (!part || !part.data || !fields) return null
  const sType = this.getType(part)
  const obj = this.getParamObject(sType)
  if (!obj) return null

  const newObj = {}
  fields.forEach(field => {
    if (obj.hasOwnProperty(field)) {
      newObj[field] = part.data[field]
    } else {
      newObj[field] = null
    }
  })
  return newObj
}

Core.prototype.getParam3 = function(part) {
  if (!part || !part.data) return null
  const sType = this.getType(part)
  const obj = this.getParamObject(sType)
  if (!obj) return null

  const newObj = {}
  // 读取基本特性(图层、颜色、线型等)
  for (const field in obj) {
    newObj[field] = part.data[field]
  }
  return newObj
}

Core.prototype.setParam = function(part, field, value) {
  if (!part || !part.data) return false
  const sType = this.getType(part)
  const obj = this.getParamObject(sType)
  if (!obj) return false
  if (obj.hasOwnProperty(field)) {
    this.diagram.startTransaction('Core.setParam')
    this.diagram.model.setDataProperty(part.data, field, value)
    this.diagram.commitTransaction('Core.setParam')
    // 记录修改的字段和值
    const datas = {}
    datas[field] = value
    // 触发事件
    this.postAsyncEntityEvents('Core.setParam()', [part], 'ObjectModified', {
      datas
    }).finishEvent()
    return true
  } else {
    return false
  }
}

Core.prototype.fastSetParam = function(parts, args) {
  if (!parts) return false

  this.diagram.startTransaction('Core.fastSetParam')
  const that = this
  parts.forEach(part => {
    if (!part || !part.data) return
    const sType = this.getType(part)
    const obj = this.getParamObject(sType)
    if (!obj) return

    const datas = {}
    for (const field in args) {
      if (obj.hasOwnProperty(field)) {
        // part.data[field] = args[field] // 无效!
        that.diagram.model.setDataProperty(part.data, field, args[field])
        // 记录修改的字段和值
        datas[field] = args[field]
      }
    }
    // 触发事件
    that
      .postAsyncEntityEvents('Core.fastSetParam()', [part], 'ObjectModified', {
        datas
      })
      .finishEvent()
  })
  this.diagram.commitTransaction('Core.fastSetParam')
  return true
}

Core.prototype.getData1 = function(part, field) {
  const __datas__ = this.getParam1(part, '__datas__')
  if (!__datas__) return null
  try {
    const obj = JSON.parse(__datas__)
    return obj[field]
  } catch (error) {
    return null
  }
}

Core.prototype.getData2 = function(part, fields) {
  const __datas__ = this.getParam1(part, '__datas__')
  if (!__datas__) return null
  try {
    const obj = JSON.parse(__datas__)
    const newObj = {}
    fields.forEach(field => {
      newObj[field] = obj[field]
    })
    return newObj
  } catch (error) {
    return null
  }
}

Core.prototype.getData3 = function(part) {
  const __datas__ = this.getParam1(part, '__datas__')
  if (!__datas__) return null
  try {
    return JSON.parse(__datas__)
  } catch (error) {
    return null
  }
}

Core.prototype.setData = function(part, field, value) {
  const __datas__ = this.getParam1(part, '__datas__')
  if (!__datas__) return false

  try {
    const obj = JSON.parse(__datas__)
    // 记录旧值
    const oldVal = obj[field]
    // 修改的新值
    obj[field] = value

    this.diagram.startTransaction('Core.setData')
    // part.data['__datas__'] = JSON.stringify(obj) // 无效!
    this.diagram.model.setDataProperty(
      part.data,
      '__datas__',
      JSON.stringify(obj)
    )
    this.diagram.commitTransaction('Core.setData')

    // 记录修改的值
    const datas = {}
    datas[field] = { oldVal, newVal: value }
    // 触发事件
    this.postAsyncEntityEvents('Core.setData()', [part], 'ExtDataModified', {
      datas
    }).finishEvent()
    return true
  } catch (error) {
    return false
  }
}

Core.prototype.fastSetData = function(parts, args) {
  if (!parts) return false

  this.diagram.startTransaction('Core.fastSetData')

  let ret = true
  const that = this
  parts.forEach(part => {
    const __datas__ = this.getParam1(part, '__datas__')
    if (!__datas__) return

    try {
      const obj = JSON.parse(__datas__)
      // 记录修改的值
      const datas = {}
      for (const field in args) {
        const oldVal = obj[field]
        obj[field] = args[field]

        // 记录被修改的值
        datas[field] = {
          oldVal,
          newVal: args[field]
        }
      }
      // part.data['__datas__'] = JSON.stringify(obj) // 无效!
      that.diagram.model.setDataProperty(
        part.data,
        '__datas__',
        JSON.stringify(obj)
      )
      // 触发事件
      that
        .postAsyncEntityEvents(
          'Core.fastSetData()',
          [part],
          'ExtDataModified',
          {
            datas
          }
        )
        .finishEvent()
    } catch (error) {
      ret = false
    }
  })
  this.diagram.commitTransaction('Core.fastSetData')
  return ret
}

Core.prototype.getValues = function(part, sType, fields) {
  if (!part || !part.data || !fields) return null
  // const sType = this.getType(part)
  if (!sType) return null

  // console.log('getValues:', part, sType, fields)
  const pobj = this.getParamObject(sType)
  if (!pobj) return null
  const dobj = this.getDataObject(sType)
  if (!dobj) return null

  // console.log(pobj)
  // console.log(dobj)
  // console.log(fields)
  // 拆分fields(一部分是param,一部分是data)
  const paramFields = []
  const dataFields = []
  fields.forEach(field => {
    if (pobj.hasOwnProperty(field)) {
      paramFields.push(field)
    } else if (dobj.hasOwnProperty(field)) {
      dataFields.push(field)
    }
  })
  // console.log(paramFields)
  // console.log(dataFields)
  const x = this.getParam2(part, paramFields)
  const y = this.getData2(part, dataFields)
  // console.log(x)
  // console.log(y)
  return {
    ...x,
    ...y
  }
}

Core.prototype.getSecondLabelField = function() {
  return this.secondLabelField
}

Core.prototype.updateSecondLabel = function(parts, field) {
  if (!parts) return
  // field无效或者属于不支持显示的字段
  if (!field || AllowShowDataList.hasOwnProperty(field) < 0) {
    // 设置当前显示的key
    this.secondLabelField = ''
    // 启动事务,修改分支数据
    this.diagram.startTransaction('Core.updateSecondLabel1')
    const that = this
    parts.forEach(link => {
      // 清空text2的值
      that.diagram.model.setDataProperty(link.data, 'text2', '')
    })
    this.diagram.commitTransaction('Core.updateSecondLabel1')
  } else {
    // 设置当前显示的field
    this.secondLabelField = field
    // 启动事务,修改分支数据
    this.diagram.startTransaction('Core.updateSecondLabel2')
    const that = this
    parts.forEach(link => {
      try {
        const data = JSON.parse(link.data['__datas__'])
        if (data.hasOwnProperty(field)) {
          // 修改text2的数据值
          that.diagram.model.setDataProperty(
            link.data,
            'text2',
            field + '=' + data[field].toString()
          )
        } else {
          // 清空text2的值
          that.diagram.model.setDataProperty(link.data, 'text2', '')
        }
      } catch (err) {}
    })
    this.diagram.commitTransaction('Core.updateSecondLabel2')
  }
}

// 是否处于动画过程中
Core.prototype.isAnimating = function() {
  return this.animation !== null && this.animation !== undefined
}

Core.prototype.configDiagram = function(val) {
  // 允许删除图形
  this.diagram.allowDelete = val
  // 允许剪切板粘贴
  this.diagram.allowClipboard = val
  // 允许复制
  this.diagram.allowCopy = val
  // 允许插入图形
  this.diagram.allowInsert = val
  // 允许绘制新分支
  this.diagram.allowLink = val
  // 允许重新连接分支
  this.diagram.allowRelink = val
  // 允许改变图形的形状和大小
  this.diagram.allowReshape = val
  this.diagram.allowResize = val
  // 允许旋转
  this.diagram.allowRotate = val
  // 允许undo/redo
  this.diagram.allowUndo = val
  // 允许文本编辑
  this.diagram.allowTextEdit = val
}

Core.prototype.playAnimation = function() {
  if (this.animation) {
    // 停止动画
    this.animation.stop()
    delete this.animation
    this.animation = null
    // 关闭动画效果
    this.diagram.startTransaction('Core.stopAnimation')
    // 打开相关设置,允许编辑操作
    this.configDiagram(true)
    // 切换分支template
    this.diagram.links.each(link => {
      this.diagram.model.setCategoryForLinkData(
        link.data,
        link.data.category.substring(8) // 去掉 animate- 前缀(8个字符)
      )
    })
    this.diagram.commitTransaction('Core.stopAnimation')
  } else {
    // 切换到鼠标pointer模式
    store.SetMode('pointer')
    // 判断分支个数
    if (this.getLinkCount() === 0) {
      MessageBox.alert('分支个数为0,无法演示动画!', '提示', {
        confirmButtonText: '确定'
      })
    } else {
      // 分支动画
      // Animate the flow in the AIRFLOWs
      this.animation = new go.Animation()
      this.animation.easing = go.Animation.EaseLinear

      this.diagram.startTransaction('Core.playAnimation')
      // 关闭相关设置,禁止编辑操作
      this.configDiagram(false)
      // 切换到动画效果的分支模板
      this.diagram.links.each(link => {
        this.diagram.model.setCategoryForLinkData(
          link.data,
          'animate-' + link.data.category
        )
        // TODO 动画中白色移动块的移动速度(应根据风速动态调整)
        this.animation.add(
          link.findObject('AIRFLOW'),
          'strokeDashOffset',
          20,
          0
        )
      })
      this.diagram.commitTransaction('Core.playAnimation')

      // Run indefinitely
      this.animation.runCount = Infinity
      this.animation.start()
    }
  }
}

export default Core
